//===============================================================================
// Microsoft Architecture Strategy Team
// LitwareHR - SaaS Sample Application
//===============================================================================
// Copyright  Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Shp.Runtime.Sitka.ServiceAgent;
using Shp.Runtime.Contract;
using System.Globalization;
using Shp.Runtime.Sitka.ServiceAgent.Entities;
using Shp.Runtime.Sitka.ServiceAgent.EntityMappers;
using Shp.Runtime.Common;
using Shp.Runtime.DataModel.Sitka.Constants;

namespace Shp.Runtime.DataModel.Sitka
{
    public class BaseDataModel
    {                
        private IRepositoryFactory repositoryFactory;

        public BaseDataModel(IRepositoryFactory factory)
        {
            repositoryFactory = factory;
        }

        public BaseDataModel()
        {
            repositoryFactory = new GenericRepositoryFactory();
        }

        public Dictionary<Guid, string> GetEntityDefList()
        {
            Dictionary<Guid, string> entityDefList = null;
            using (Repository<GenericExtensibleEntity> repository = repositoryFactory.MetadataRepository)
            {
                IEnumerable<GenericExtensibleEntity> queryResult = repository.Search(String.Format(CultureInfo.InvariantCulture, Constants.SitkaMetadataQueries.GetEntityDefList));
                entityDefList = new Dictionary<Guid, string>();
                foreach (GenericExtensibleEntity entityDef in queryResult)
                {
                    entityDefList.Add(entityDef.Id, entityDef.GetFieldValue(EntityFields.Name));
                }
            }
            return entityDefList;
        }

        public GuidStringPair[] GetTypeList()
        {
            List<GuidStringPair> typeList = null;
            using (Repository<GenericExtensibleEntity> repository = repositoryFactory.MetadataRepository)
            {
                IEnumerable<GenericExtensibleEntity> queryResult = repository.Search(String.Format(CultureInfo.InvariantCulture, Constants.SitkaMetadataQueries.GetTypeList));
                typeList = new List<GuidStringPair>();
                foreach (GenericExtensibleEntity type in queryResult)
                {
                    typeList.Add(new GuidStringPair(type.Id, type.GetFieldValue(EntityFields.Name)));
                }
            }
            return typeList.ToArray();
        }

        private Guid GetEntityIdByName(string entityDefName)
        {
            Guid entityDefId = Guid.Empty;
            using (Repository<GenericExtensibleEntity> repository = repositoryFactory.MetadataRepository)
            {
                IEnumerable<GenericExtensibleEntity> entityDef = repository.Search(String.Format(CultureInfo.InvariantCulture, Constants.SitkaMetadataQueries.GetEntityDefByName, entityDefName));
                if (entityDef != null)
                {
                    foreach (GenericExtensibleEntity entity in entityDef)
                    {
                        if (entity.GetFieldValue(EntityFields.Name) == entityDefName)
                        {
                            entityDefId = entity.Id;
                            break;
                        }
                    }
                }
            }
            return entityDefId;
        }

        private string GetTypeName(GuidStringPair[] types, Guid typeId)
        {
            string typeName = string.Empty;
            foreach (GuidStringPair type in types)
            {
                if (type.Key.Equals(typeId))
                {
                    typeName = type.Value;
                }
            }
            return typeName;
        }

        public EntityField[] GetEntityDefFields(Guid tenantId, Guid entityDefId)
        {
            List<EntityField> entityFields = null;
            using (Repository<GenericExtensibleEntity> repository = repositoryFactory.MetadataRepository)
            {
                GuidStringPair[] typeList = GetTypeList();
                IEnumerable<GenericExtensibleEntity> queryResult = repository.Search(String.Format(CultureInfo.InvariantCulture, Constants.SitkaMetadataQueries.GetEntityDefFields, entityDefId, tenantId));
                entityFields = new List<EntityField>();
                foreach (GenericExtensibleEntity entityDefField in queryResult)
                {
                    EntityField field = new EntityField();
                    field.Id = entityDefField.Id;
                    field.Name = entityDefField.GetFieldValue(EntityFields.Name);
                    field.TypeId = new Guid(entityDefField.GetFieldValue(EntityFields.EntityDefFieldTypeId));
                    bool isShared;
                    bool isVisibleFromAppForm;
                    if (bool.TryParse(entityDefField.GetFieldValue(EntityFields.EntityDefFieldIsShared), out isShared))
                    {
                        field.IsShared = isShared;
                    }
                    if (bool.TryParse(entityDefField.GetFieldValue(EntityFields.EntityDefFieldIsVisibleFromAppForm), out isVisibleFromAppForm))
                    {
                        field.IsVisibleFromApplicationForm = isVisibleFromAppForm;
                    }

                    field.TypeName = GetTypeName(typeList, field.TypeId);
                    entityFields.Add(field);
                }
            }
            return entityFields.ToArray();
        }

        public EntityField[] GetEntityDefFields(Guid tenantId, string entityDefName)
        {
            Guid entityDefId = GetEntityIdByName(entityDefName);
            return GetEntityDefFields(tenantId, entityDefId);            
        }

        public EntityField[] GetEntityDefFields(string tenantAlias, string entityDefName)
        {
            SystemDataModel tenantAccess = new SystemDataModel(repositoryFactory);
            Guid tenantId = tenantAccess.GetTenantId(tenantAlias);
            return GetEntityDefFields(tenantId, entityDefName);            
        }

        public void AddEntityDefField(Guid tenantId, Guid entityDefId, EntityField field, Repository<GenericExtensibleEntity> repository)
        {
            GenericExtensibleEntity newField = new GenericExtensibleEntity();
            if (Guid.Empty.Equals(field.Id))
            {
                field.Id = Guid.NewGuid();
            }

            newField.Id = field.Id;
            newField.Kind = EntityKinds.EntityDefField;
            newField.Fields.Add(new Field(EntityFields.EntityDefId, entityDefId.ToString()));
            newField.Fields.Add(new Field(EntityFields.Name, field.Name));
            newField.Fields.Add(new Field(EntityFields.EntityDefFieldTypeId, field.TypeId.ToString()));
            newField.Fields.Add(new Field(EntityFields.EntityDefFieldIsShared, field.IsShared));
            newField.Fields.Add(new Field(EntityFields.EntityDefFieldIsVisibleFromAppForm, field.IsVisibleFromApplicationForm));
            newField.Fields.Add(new Field(EntityFields.TenantId, tenantId.ToString()));

            repository.Insert(newField);
        }

        public void UpdateEntityDef(Guid tenantId, Guid entityDefId, EntityField[] newFields)
        {
            EntityField[] oldFields = GetEntityDefFields(tenantId, entityDefId);
            if (oldFields != null)
            {
                List<EntityField> oldFieldsList = new List<EntityField>(oldFields);
                List<EntityField> oldFieldsCustom = oldFieldsList.FindAll(entity => !entity.IsShared);

                List<EntityField> newFieldsList = new List<EntityField>(newFields);                
                List<EntityField> newFieldsCustom = newFieldsList.FindAll(entity => !entity.IsShared);

                using (Repository<GenericExtensibleEntity> repository = repositoryFactory.MetadataRepository)
                {                
                    foreach (EntityField deletingField in oldFieldsCustom)
                    {
                        repository.Delete(deletingField.Id);
                    }

                    foreach (EntityField creatingField in newFieldsCustom)
                    {
                        AddEntityDefField(tenantId, entityDefId, creatingField, repository);
                    }
                }
            }            
        }

        public Dictionary<Guid, string> GetViewDefList()
        {
            Dictionary<Guid, string> viewDefList = null;
            using (Repository<GenericExtensibleEntity> repository = repositoryFactory.MetadataRepository)
            {
                IEnumerable<GenericExtensibleEntity> queryResult = repository.Search(String.Format(CultureInfo.InvariantCulture, Constants.SitkaMetadataQueries.GetViewDefList));
                viewDefList = new Dictionary<Guid, string>();
                foreach (GenericExtensibleEntity viewDef in queryResult)
                {
                    viewDefList.Add(viewDef.Id, viewDef.GetFieldValue(EntityFields.Name));
                }
            }
            return viewDefList;
        }        

        public StringPair[] GetViewDefFields(Guid tenantId, Guid viewDefId)
        {
            List<StringPair> viewFields = null;
            using (Repository<GenericExtensibleEntity> repository = repositoryFactory.MetadataRepository)
            {
                IEnumerable<GenericExtensibleEntity> queryResult = repository.Search(String.Format(CultureInfo.InvariantCulture, Constants.SitkaMetadataQueries.GetViewDefFields, viewDefId, tenantId));
                viewFields = new List<StringPair>();                
                foreach (GenericExtensibleEntity viewDefField in queryResult)
                {
                    StringPair field = new StringPair();
                    field.Key = viewDefField.GetFieldValue(EntityFields.Label);
                    field.Value = viewDefField.GetFieldValue(EntityFields.Name);
                    viewFields.Add(field);
                }
            }
            return viewFields.ToArray();
        }

        private String[] GetIdsViewDefFields(Guid tenantId, Guid viewDefId)
        {
            List<String> viewFields = null;
            using (Repository<GenericExtensibleEntity> repository = repositoryFactory.MetadataRepository)
            {
                IEnumerable<GenericExtensibleEntity> queryResult = repository.Search(String.Format(CultureInfo.InvariantCulture, Constants.SitkaMetadataQueries.GetViewDefFields, viewDefId, tenantId));
                viewFields = new List<String>();
                foreach (GenericExtensibleEntity viewDefField in queryResult)
                {                    
                    viewFields.Add(viewDefField.Id.ToString());
                }
            }
            return viewFields.ToArray();
        }

        private Guid GetViewDefIdByName(string viewDefName)
        {
            Guid viewDefId = Guid.Empty;
            Dictionary<Guid, string> viewDefList = GetViewDefList();
            foreach (Guid key in viewDefList.Keys)
            {
                if (viewDefList[key] == viewDefName)
                {
                    viewDefId = key;
                    break;
                }
            }
            return viewDefId;
        }

        public StringPair[] GetViewDefFields(string tenantAlias, string viewDefName)
        {
            Guid viewDefId = GetViewDefIdByName(viewDefName);
            return GetViewDefFields(tenantAlias, viewDefId);
        }

        public StringPair[] GetViewDefFields(string tenantAlias, Guid viewDefId)
        {
            SystemDataModel tenantAccess = new SystemDataModel(repositoryFactory);
            Guid tenantId = tenantAccess.GetTenantId(tenantAlias);
            return GetViewDefFields(tenantId, viewDefId);
        }

        public Guid AddInstance(Guid tenantId, string entityDefName, EntityField[] fields)
		{
			Guid instanceId = Guid.NewGuid();
			using (Repository<GenericExtensibleEntity> repository = new Repository<GenericExtensibleEntity>(tenantId.ToString(), new GenericExtensibleEntityMapper()))
			{
				GenericExtensibleEntity extensibleEntity = new GenericExtensibleEntity(entityDefName, instanceId);

				foreach (EntityField item in fields)
				{
					Field field = new Field(item.Name, item.Value);
					extensibleEntity.Fields.Add(field);
				}
				repository.Insert(extensibleEntity);
			}
			return instanceId;
		}

		public StringPair[][] GetInstanceList(Guid tenantId, string entityDefName, Guid[] listToRetrieve)
		{
			List<StringPair[]> list = new List<StringPair[]>();
			using (Repository<GenericExtensibleEntity> repository = new Repository<GenericExtensibleEntity>(tenantId.ToString(), new GenericExtensibleEntityMapper()))
			{
				foreach (Guid Item in listToRetrieve)
				{
					GenericExtensibleEntity entityResult = repository.GetById(Item);
					List<StringPair> record;
					record = new List<StringPair>();

					record.Add(new StringPair("id",entityResult.Id.ToString()));

					foreach (Field item in entityResult.Fields)
					{
						StringPair pair = new StringPair(item.Name, item.Value.ToString());
						record.Add(pair);
					}
					list.Add(record.ToArray());
				}
			}
			return list.ToArray();
		}

        private void AddViewDefField(Guid tenantId, Guid viewDefId, StringPair field, int oridinal, Repository<GenericExtensibleEntity> repository)
        {
            GenericExtensibleEntity newField = new GenericExtensibleEntity();
            newField.Id = Guid.NewGuid();
            newField.Kind = EntityKinds.ViewDefField;

            newField.Fields.Add(new Field(EntityFields.ViewDefId, viewDefId.ToString()));
            newField.Fields.Add(new Field(EntityFields.Label, field.Key));
            newField.Fields.Add(new Field(EntityFields.Name, field.Value));
            newField.Fields.Add(new Field(EntityFields.Ordinal, oridinal.ToString(CultureInfo.InvariantCulture)));
            newField.Fields.Add(new Field(EntityFields.TenantId, tenantId.ToString()));

            repository.Insert(newField);
        }

        public void UpdateViewDef(Guid tenantId, Guid viewDefId, StringPair[] fields)
        {
            String[] deleteingViewDefFields = GetIdsViewDefFields(tenantId, viewDefId);
            using (Repository<GenericExtensibleEntity> repository = repositoryFactory.MetadataRepository)
            {
                if (deleteingViewDefFields != null)
                {
                    foreach (String oldIdField in deleteingViewDefFields)
                    {
                        repository.Delete(new Guid(oldIdField));
                    }
                }
                int ordinal = 0;
                foreach (StringPair newField in fields)
                {
                    AddViewDefField(tenantId, viewDefId, newField, ordinal++, repository);                    
                }
            }
        }
    }
}
